#!/usr/bin/env perl

=head1 NAME

Scorer 

=head1 SYNOPSIS

use Scorer;

my $scorer = Scorer->new(maxRMSD=>5, scoreRangeMax=>100);
$scorer->measureSubcomplex($mySubcomplex);

=head1 DESCRIPTION

 Evaluates the quality of a subcomplex. The scoring is currently based on the RMSD of the mapped domains that
 constitute the subcomplex. The Scorer also calculates the total deviations among superposed domains. The domains
 as represented in a subcomplex are not superposed. Each time a subcomplex is evaluated or 'measured' the Scorer
 superposes the domains and applies the similarity metrics (currently RMSD, total deviation). The actual superposition
 and calculation of RMSD is done with the corresponding methods of the sbglib RMSD-package. The score is based on the 
 RMSD as compared to 'maxRMSD' and is the range of 0 to 'scoreRangeMax'. Scorer does not return any values, it directly
 sets the corresponding attributes of the subcomplex that is measured.

=head1 REQUIRES

sbglib, PDL

=head1 AUTHOR

Philipp Harth <phil.harth@gmail.com>

=cut

use PDL;
use PDL::Ufunc;
use Log::Log4perl qw(:easy);

use SBG::U::RMSD;

package Scorer;
use Moose;

has 'maxRMSD' => (is => 'ro', default=>5);
has 'scoreRangeMax' => (is => 'ro', default=>100);

=head2 measureSubcomplex

 Function: determines deviation, RMSD and score of a subcomplex
 Example : 
 Returns : 
 Args    : subcomplex to be measured
 
 Superposes the domains from two complexes that constitute a shared subcomplex.
 Total deviation, local deviations and RMSD are calculated and the corresponding
 attributes of the subcomplex are set to these values. A score is assigned to the
 subcomplex based on its RMSD as compared to the maximum RMSD. 
 
=cut
sub measureSubcomplex{
	my ($self, $s) = @_;
	
	my $domainsA = PDL->new([]);
	my $domainsB = PDL->new([]);
	
	#turn coordinates into correct format
	#here only the centroids are used. Change the code below, if the orientations are 
	#also avaialable (instead of domain->centroid, use domain->coords with all 7 points).  
	for my $domain (@{$s->domainsA}){
		$domainsA = $domainsA->glue(1, $domain->centroid);
	}
	for my $domain (@{$s->domainsB}){
		$domainsB = $domainsB->glue(1, $domain->centroid);
	}
	#catch numeric errors due to coordiates unsuited for superposition	
	$SIG{__WARN__} = sub {
		my $logger = Log::Log4perl::get_logger();
		if(defined($logger)){
			$logger->info("numeric error");
		}else{
			print "numeric error \n";
		}
		return 0;
	};	
	
	#transpose and calculate RMSD
	my $A = SBG::U::RMSD::superpose($domainsA, $domainsB);
	my $r = SBG::U::RMSD::rmsd($domainsA, $domainsB);
	
	#set rmsd, score, total deviation, local deviation and transformation matrix in subcomplex
	$s->score(0);
	$s->deviation(-1);
	$s->rmsd(-1);
	
	if(defined($r) && defined($A)){
		$s->rmsd($r);
		$s->transformationMatrix($A);
		$s->score($self->scoreRMSD($s->rmsd));
		#calculate local deviations and total deviation
		#this should better be outsourced as a separate function into sbglib and may be 
		#combined with the rmsd function that calculates the total deviations as a by-product
		my $localDeviations = sqrt(PDL::Ufunc::sumover(($domainsA-$domainsB)**2));
		my $totalDeviation = PDL::Ufunc::sum($localDeviations);
		$s->localDeviations($localDeviations);
		$s->deviation($totalDeviation);
	}
	return 0;
}


=head2 scoreRMSD

 Function: calculates a score for a given RMSD
 Example : 
 Returns : score 
 Args    : RMSD
 
 The range of the score is 0 to 'scoreRangeMax' (default value 100).
 
=cut
sub scoreRMSD{
	my ($self, $rmsd)=@_;
		if($rmsd < $self->maxRMSD){
			return ($self->scoreRangeMax*(1-$rmsd/$self->maxRMSD));
		} else {
			return 0;
		}
}


no Moose;
1;